#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_BUF_SIZE 256
#define MAX_QUEUE_SIZE 10
#define BUF_LEN 50
#define EPOLL_WAIT_EVENT_LEN 10
#define EPOLL_WAIT_MAX_EVENT 10
#define EPOLL_WAIT_TIMEOUT 1000

int m_sockfd, ep_sockfd;

int setnonblocking(int fd) {
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void sigint_handler(int sig) {
    printf("\nServer received SIGINT, shutting down...\n");
    close(m_sockfd);
    close(ep_sockfd);
    exit(1);
}

/* Usage：./select_demo [port] */

int main(int argc, char *argv[]) {
    int portno, i, n, num, sockfd, newsockfd;
    char buf[BUF_LEN], cli_addr_buf[INET_ADDRSTRLEN+1];
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t clilen;
    struct epoll_event event, events[EPOLL_WAIT_EVENT_LEN];

    if (argc < 2) {
        fprintf(stderr,"ERROR: no port provided\n");
        goto exit;
    }

    /* Create socket */
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_sockfd < 0) {
        perror("socket");
        goto exit;
    }

    /* Create network address */
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    portno = atoi(argv[1]);
    serv_addr.sin_port = htons(portno);

    /* Bind socket to specific network address */
    if (bind(m_sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("bind");
        goto close_mfd;
    }
        
    /* Create epoll */
    ep_sockfd = epoll_create(10);
    if (ep_sockfd < 0) {
        perror("epoll_create");
        goto close_mfd;
    }

    /* Add m_sockfd to epoll */
    event.data.fd = m_sockfd;
    event.events = EPOLLIN | EPOLLET; // 边缘触发
    if (epoll_ctl(ep_sockfd, EPOLL_CTL_ADD, m_sockfd, &event)) {
        perror("epoll_ctl");
        goto close_epfd;
    }
    
    /* Start listen on socket */
    if (listen(m_sockfd, MAX_QUEUE_SIZE)) {
        perror("listen");
        goto close_epfd;
    }

    clilen = sizeof(cli_addr);
    printf("Server start listening on port %d\n", portno);

    /* Running server */
    while(1) {
        /* Wait on epoll */
        num = epoll_wait(ep_sockfd, events, EPOLL_WAIT_MAX_EVENT, EPOLL_WAIT_TIMEOUT);
        if (num < 0) {
            perror("epoll_wait");
            break;
        }
        
        for (i = 0; i < num; ++i) {
            sockfd = events[i].data.fd;
            if (sockfd == m_sockfd) {
                newsockfd = accept(m_sockfd, (struct sockaddr*)&cli_addr, &clilen);
                if (newsockfd < 0) {
                    perror("accept");
                    continue;
                }

                /* Add new socket to epoll */
                event.data.fd = newsockfd;
                event.events = EPOLLIN | EPOLLET;
                if (epoll_ctl(ep_sockfd, EPOLL_CTL_ADD, newsockfd, &event)) {
                    perror("epoll_ctl");
                    close(newsockfd);
                    continue;
                }

                bzero(cli_addr_buf, sizeof(cli_addr_buf));
                inet_ntop(AF_INET, &cli_addr.sin_addr, cli_addr_buf, INET_ADDRSTRLEN);
                portno = ntohs(cli_addr.sin_port);
                printf("Server receive connection from %s, port %d\n", cli_addr_buf, portno);

                // setnonblocking(newsockfd);
            } else if (events[i].events & EPOLLIN) {
                memset(buf, 0, sizeof(buf));
                while ((n = read(sockfd, buf, sizeof(buf))) > 0) {
                    printf("%s", buf);
                    memset(buf, 0, sizeof(buf));;
                }

                if (n < 0) {
                    perror("read");
                    epoll_ctl(ep_sockfd, EPOLL_CTL_DEL, sockfd, NULL);
                    close(sockfd);
                    continue;
                }
            } else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                epoll_ctl(ep_sockfd, EPOLL_CTL_DEL, sockfd, NULL);
                close(sockfd);
            }
        }
    }

    printf("Closing server\n");
    close(m_sockfd);
    close(ep_sockfd);
    return 0;

close_epfd:
    close(ep_sockfd);
close_mfd:
    close(m_sockfd);
exit:
    exit(1);
}